package com.fengxu.netty.groupchat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;

// 群聊服务器
public class GroupChatServer {

    private int port;

    public GroupChatServer(int port){
        this.port = port;
    }

    public void run() throws Exception{
        // 创建线程组
        NioEventLoopGroup boosGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        // 创建服务器启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(boosGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 向pipeline中添加String的解码器
                            ch.pipeline().addLast("decode",new StringDecoder());
                            // 向pipeline中添加String编码器
                            ch.pipeline().addLast("encode",new StringEncoder());
                            // 向pipeline中加入自己的业务处理器
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
        } catch (Exception e){
            System.out.println("出现异常,关闭服务器");
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
        ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
        channelFuture.addListener(future -> {
            if(future.isSuccess()){
                System.out.println("服务器开启成功!");
            } else {
                System.out.println("服务器开启失败!");
            }
        });
        channelFuture.channel().closeFuture().sync();
    }

    // 业务处理的Handler
    private static class ServerHandler extends SimpleChannelInboundHandler<String> {

        // 定义一个管理所有channel的group,并设置其为单例，其中存所有在线的用户对象
        // 该对象必须是静态的才能保持其是单例
        private static ChannelGroup globalChannelChannel = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        // 时间格式化对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");


        // 客户端第一次连接时会触发,第一个被执行的事件
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            // 发送信息给所有用户通知上线,writeAndFlush会自定发送给组内所有的channel
            globalChannelChannel.writeAndFlush(String.format("客户端[%s]加入群聊!",ctx.channel().remoteAddress()));
            // 将新用户添加到全局的globalChannelChannel中
            globalChannelChannel.add(ctx.channel());
        }

        // 客户端处于活动状态时会被触发,提示上线
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(String.format("客户端%s上线!", ctx.channel().remoteAddress()));
        }

        // 客户端不活动时触发,提示下线
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(String.format("客户端%s离线了!", ctx.channel().remoteAddress()));
        }

        // 用户离线时触发
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            // 通知所有客户端，某客户端下线!
            // handlerRemoved触发了后，会自动移除globalChannelChannel中的相应通道，不需要手动移除
            globalChannelChannel.writeAndFlush(String.format("客户端[%s],断开连接了!", ctx.channel().remoteAddress()));
        }

        // 读取事件事件
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            Channel channel = ctx.channel();
            System.out.println(String.format("接收到客户端[%s]：%s", channel.remoteAddress(),msg));
            // 转发消息
            globalChannelChannel.forEach(ch -> {
                if(ch != channel){
                    ch.writeAndFlush(String.format("客户端[%s]:%s", channel.remoteAddress(),msg));
                } else {
                    ch.writeAndFlush(String.format("自己[%s]:%s", channel.remoteAddress(),msg));
                }
            });
        }

        // 发送异常
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();
        }
    }

    public static void main(String[] args) throws Exception {
        GroupChatServer server = new GroupChatServer(6666);
        server.run();
    }

}